#include "net_johnpwood_android_standuptimer_jni_DexdumpJNI.h"

#include <zlib.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <getopt.h>
#include <errno.h>
#include <limits.h>  /* for CHAR_BIT */
#include <assert.h>
#include <android/log.h>

#define LOG_TAG "MYDUMPJNI"
#define LOGI(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define BUF 512
#define DEX_MAGIC       "dex\n"
#define DEX_OPT_MAGIC   "dey\n"
#define DEX_MAGIC_VERS  "036\0"
#define DEX_OPT_MAGIC_VERS  "036\0"
#define DEX_MAGIC_VERS_API_13  "035\0"

#ifndef O_BINARY
#define O_BINARY 0
#endif

#ifndef __BYTE_ORDER
# error "byte ordering not defined"
#endif

#ifdef PAGE_SHIFT
#define SYSTEM_PAGE_SIZE        (1<<PAGE_SHIFT)
#else
#define SYSTEM_PAGE_SIZE        4096
#endif

/*
 * These match the definitions in the VM specification.
 */
typedef uint8_t u1;
typedef uint16_t u2;
typedef uint32_t u4;
typedef uint64_t u8;
typedef int8_t s1;
typedef int16_t s2;
typedef int32_t s4;
typedef int64_t s8;

typedef u1 OpcodeFlags;
typedef u1 InstructionWidth;

#define kPackedSwitchSignature  0x0100
#define kSparseSwitchSignature  0x0200
#define kArrayDataSignature     0x0300

#define kNumPackedOpcodes 0x100

enum {
	kDexEndianConstant = 0x12345678, /* the endianness indicator */
	kDexNoIndex = 0xffffffff, /* not a valid index value */
};

enum { kSHA1DigestLen = 20,
       kSHA1DigestOutputLen = kSHA1DigestLen*2 +1 };

enum {
	ACC_PUBLIC = 0x00000001,       // class, field, method, ic
	ACC_PRIVATE = 0x00000002,       // field, method, ic
	ACC_PROTECTED = 0x00000004,       // field, method, ic
	ACC_STATIC = 0x00000008,       // field, method, ic
	ACC_FINAL = 0x00000010,       // class, field, method, ic
	ACC_SYNCHRONIZED = 0x00000020,       // method (only allowed on natives)
	ACC_SUPER = 0x00000020,       // class (not used in Dalvik)
	ACC_VOLATILE = 0x00000040,       // field
	ACC_BRIDGE = 0x00000040,       // method (1.5)
	ACC_TRANSIENT = 0x00000080,       // field
	ACC_VARARGS = 0x00000080,       // method (1.5)
	ACC_NATIVE = 0x00000100,       // method
	ACC_INTERFACE = 0x00000200,       // class, ic
	ACC_ABSTRACT = 0x00000400,       // class, method, ic
	ACC_STRICT = 0x00000800,       // method
	ACC_SYNTHETIC = 0x00001000,       // field, method, ic
	ACC_ANNOTATION = 0x00002000,       // class, ic (1.5)
	ACC_ENUM = 0x00004000,       // class, field, ic (1.5)
	ACC_CONSTRUCTOR = 0x00010000,       // method (Dalvik only)
	ACC_DECLARED_SYNCHRONIZED = 0x00020000,       // method (Dalvik only)
	ACC_CLASS_MASK = (ACC_PUBLIC | ACC_FINAL | ACC_INTERFACE | ACC_ABSTRACT
			| ACC_SYNTHETIC | ACC_ANNOTATION | ACC_ENUM),
	ACC_INNER_CLASS_MASK = (ACC_CLASS_MASK | ACC_PRIVATE | ACC_PROTECTED
			| ACC_STATIC),
	ACC_FIELD_MASK = (ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC
			| ACC_FINAL | ACC_VOLATILE | ACC_TRANSIENT | ACC_SYNTHETIC
			| ACC_ENUM),
	ACC_METHOD_MASK = (ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC
			| ACC_FINAL | ACC_SYNCHRONIZED | ACC_BRIDGE | ACC_VARARGS
			| ACC_NATIVE | ACC_ABSTRACT | ACC_STRICT | ACC_SYNTHETIC
			| ACC_CONSTRUCTOR | ACC_DECLARED_SYNCHRONIZED),
};

enum {
	kDexVisibilityBuild = 0x00, /* annotation visibility */
	kDexVisibilityRuntime = 0x01,
	kDexVisibilitySystem = 0x02,

	kDexAnnotationByte = 0x00,
	kDexAnnotationShort = 0x02,
	kDexAnnotationChar = 0x03,
	kDexAnnotationInt = 0x04,
	kDexAnnotationLong = 0x06,
	kDexAnnotationFloat = 0x10,
	kDexAnnotationDouble = 0x11,
	kDexAnnotationString = 0x17,
	kDexAnnotationType = 0x18,
	kDexAnnotationField = 0x19,
	kDexAnnotationMethod = 0x1a,
	kDexAnnotationEnum = 0x1b,
	kDexAnnotationArray = 0x1c,
	kDexAnnotationAnnotation = 0x1d,
	kDexAnnotationNull = 0x1e,
	kDexAnnotationBoolean = 0x1f,

	kDexAnnotationValueTypeMask = 0x1f, /* low 5 bits */
	kDexAnnotationValueArgShift = 5,
};

/* encode the result of unzipping to a file */
enum UnzipToFileResult {
	kUTFRSuccess = 0,
	kUTFRGenericFailure,
	kUTFRBadArgs,
	kUTFRNotZip,
	kUTFRNoClassesDex,
	kUTFROutputFileProblem,
	kUTFRBadZip,
};

enum {
	kDexTypeHeaderItem = 0x0000,
	kDexTypeStringIdItem = 0x0001,
	kDexTypeTypeIdItem = 0x0002,
	kDexTypeProtoIdItem = 0x0003,
	kDexTypeFieldIdItem = 0x0004,
	kDexTypeMethodIdItem = 0x0005,
	kDexTypeClassDefItem = 0x0006,
	kDexTypeMapList = 0x1000,
	kDexTypeTypeList = 0x1001,
	kDexTypeAnnotationSetRefList = 0x1002,
	kDexTypeAnnotationSetItem = 0x1003,
	kDexTypeClassDataItem = 0x2000,
	kDexTypeCodeItem = 0x2001,
	kDexTypeStringDataItem = 0x2002,
	kDexTypeDebugInfoItem = 0x2003,
	kDexTypeAnnotationItem = 0x2004,
	kDexTypeEncodedArrayItem = 0x2005,
	kDexTypeAnnotationsDirectoryItem = 0x2006,
};

enum {
	DBG_END_SEQUENCE = 0x00,
	DBG_ADVANCE_PC = 0x01,
	DBG_ADVANCE_LINE = 0x02,
	DBG_START_LOCAL = 0x03,
	DBG_START_LOCAL_EXTENDED = 0x04,
	DBG_END_LOCAL = 0x05,
	DBG_RESTART_LOCAL = 0x06,
	DBG_SET_PROLOGUE_END = 0x07,
	DBG_SET_EPILOGUE_BEGIN = 0x08,
	DBG_SET_FILE = 0x09,
	DBG_FIRST_SPECIAL = 0x0a,
	DBG_LINE_BASE = -4,
	DBG_LINE_RANGE = 15,
};

enum {
    kDexChunkClassLookup            = 0x434c4b50,   /* CLKP */
    kDexChunkRegisterMaps           = 0x524d4150,   /* RMAP */
    kDexChunkEnd                    = 0x41454e44,   /* AEND */
};

enum {
    kDexParseDefault            = 0,
    kDexParseVerifyChecksum     = 1,
    kDexParseContinueOnError    = (1 << 1),
};

struct DexOptHeader {
	u1 magic[8]; /* includes version number */
	u4 dexOffset; /* file offset of DEX header */
	u4 dexLength;
	u4 depsOffset; /* offset of optimized DEX dependency table */
	u4 depsLength;
	u4 optOffset; /* file offset of optimized data tables */
	u4 optLength;
	u4 flags; /* some info flags */
	u4 checksum; /* adler32 checksum covering deps/opt */
	/* pad for 64-bit alignment if necessary */
};

struct DexHeader {
	u1 magic[8]; /* includes version number */
	u4 checksum; /* adler32 checksum */
	u1 signature[kSHA1DigestLen]; /* SHA-1 hash */
	u4 fileSize; /* length of entire file */
	u4 headerSize; /* offset to start of next section */
	u4 endianTag;
	u4 linkSize;
	u4 linkOff;
	u4 mapOff;
	u4 stringIdsSize;
	u4 stringIdsOff;
	u4 typeIdsSize;
	u4 typeIdsOff;
	u4 protoIdsSize;
	u4 protoIdsOff;
	u4 fieldIdsSize;
	u4 fieldIdsOff;
	u4 methodIdsSize;
	u4 methodIdsOff;
	u4 classDefsSize;
	u4 classDefsOff;
	u4 dataSize;
	u4 dataOff;
};

struct DexStringId {
	u4 stringDataOff; /* file offset to string_data_item */
};

struct DexTypeId {
	u4 descriptorIdx; /* index into stringIds list for type descriptor */
};

struct DexFieldId {
	u2 classIdx; /* index into typeIds list for defining class */
	u2 typeIdx; /* index into typeIds for field type */
	u4 nameIdx; /* index into stringIds for field name */
};

struct DexMethodId {
	u2 classIdx; /* index into typeIds list for defining class */
	u2 protoIdx; /* index into protoIds for method prototype */
	u4 nameIdx; /* index into stringIds for method name */
};

struct DexProtoId {
	u4 shortyIdx; /* index into stringIds for shorty descriptor */
	u4 returnTypeIdx; /* index into typeIds list for return type */
	u4 parametersOff; /* file offset to type_list for parameter types */
};

struct DexClassDef {
	u4 classIdx; /* index into typeIds for this class */
	u4 accessFlags;
	u4 superclassIdx; /* index into typeIds for superclass */
	u4 interfacesOff; /* file offset to DexTypeList */
	u4 sourceFileIdx; /* index into stringIds for source file name */
	u4 annotationsOff; /* file offset to annotations_directory_item */
	u4 classDataOff; /* file offset to class_data_item */
	u4 staticValuesOff; /* file offset to DexEncodedArray */
};

struct DexCode {
	u2 registersSize;
	u2 insSize;
	u2 outsSize;
	u2 triesSize;
	u4 debugInfoOff; /* file offset to debug info stream */
	u4 insnsSize; /* size of the insns array, in u2 units */
	u2 insns[1];
	/* followed by optional u2 padding */
	/* followed by try_item[triesSize] */
	/* followed by uleb128 handlersSize */
	/* followed by catch_handler_item[handlersSize] */
};

struct DexLink {
	u1 bleargh;
};

struct DexClassLookup {
	int size;                       // total size, including "size"
	int numEntries;                 // size of table[]; always power of 2
	struct {
		u4 classDescriptorHash;    // class descriptor hash code
		int classDescriptorOffset;  // in bytes, from start of DEX
		int classDefOffset;         // in bytes, from start of DEX
	} table[1];
};

struct DexFile {
	/* directly-mapped "opt" header */
	const DexOptHeader* pOptHeader;

	/* pointers to directly-mapped structs and arrays in base DEX */
	const DexHeader* pHeader;
	const DexStringId* pStringIds;
	const DexTypeId* pTypeIds;
	const DexFieldId* pFieldIds;
	const DexMethodId* pMethodIds;
	const DexProtoId* pProtoIds;
	const DexClassDef* pClassDefs;
	const DexLink* pLinkData;

	/*
	 * These are mapped out of the "auxillary" section, and may not be
	 * included in the file.
	 */
	const DexClassLookup* pClassLookup;
	const void* pRegisterMapPool;       // RegisterMapClassPool

	/* points to start of DEX file data */
	const u1* baseAddr;

	/* track memory overhead for auxillary structures */
	int overhead;

	/* additional app-specific data structures associated with the DEX */
	//void*               auxData;
};

/*
 * Use this to keep track of mapped segments.
 */
struct MemMapping {
	void* addr; /* start of data */
	size_t length; /* length of data */

	void* baseAddr; /* page-aligned base address */
	size_t baseLength; /* length of mapping */
};

struct DexClassDataHeader {
	u4 staticFieldsSize;
	u4 instanceFieldsSize;
	u4 directMethodsSize;
	u4 virtualMethodsSize;
};

struct DexField {
	u4 fieldIdx; /* index to a field_id_item */
	u4 accessFlags;
};

struct DexMethod {
	u4 methodIdx; /* index to a method_id_item */
	u4 accessFlags;
	u4 codeOff; /* file offset to a code_item */
};

struct DexClassData {
	DexClassDataHeader header;
	DexField* staticFields;
	DexField* instanceFields;
	DexMethod* directMethods;
	DexMethod* virtualMethods;
};

struct node{
	char classname[BUF];
	node* nextnode;
};

struct node* classlist = NULL;
struct node* current = NULL;

int modifyNum;

static inline u4 fetch_u4_impl(u4 offset, const u2* insns) {
    return insns[offset] | ((u4) insns[offset+1] << 16);
}

#define FETCH(_offset)      (insns[(_offset)])
#define FETCH_u4(_offset)   (fetch_u4_impl((_offset), insns))
#define INST_A(_inst)       (((u2)(_inst) >> 8) & 0x0f)
#define INST_B(_inst)       ((u2)(_inst) >> 12)
#define INST_AA(_inst)      ((_inst) >> 8)

typedef enum {
	SAFE_IOP_TYPE_S32 = 1,
	SAFE_IOP_TYPE_U32,
	SAFE_IOP_TYPE_DEFAULT = SAFE_IOP_TYPE_S32,
	} safe_type_t;

int readUnsignedLeb128(const u1** pStream) {
	const u1* ptr = *pStream;
	int result = *(ptr++);

	if (result > 0x7f) {
		int cur = *(ptr++);
		result = (result & 0x7f) | ((cur & 0x7f) << 7);
		if (cur > 0x7f) {
			cur = *(ptr++);
			result |= (cur & 0x7f) << 14;
			if (cur > 0x7f) {
				cur = *(ptr++);
				result |= (cur & 0x7f) << 21;
				if (cur > 0x7f) {
					/*
					 * Note: We don't check to see if cur is out of
					 * range here, meaning we tolerate garbage in the
					 * high four-order bits.
					 */
					cur = *(ptr++);
					result |= cur << 28;
				}
			}
		}
	}

	*pStream = ptr;
	return result;
}

int readAndVerifyUnsignedLeb128(const u1** pStream, const u1* limit,
		bool* okay) {
	const u1* ptr = *pStream;
	int result = readUnsignedLeb128(pStream);

	if (((limit != NULL) && (*pStream > limit))
			|| (((*pStream - ptr) == 5) && (ptr[4] > 0x0f))) {
		*okay = false;
	}

	return result;
}

static const char* primitiveTypeLabel(char typeChar)
{
    switch (typeChar) {
    case 'B':   return "byte";
    case 'C':   return "char";
    case 'D':   return "double";
    case 'F':   return "float";
    case 'I':   return "int";
    case 'J':   return "long";
    case 'S':   return "short";
    case 'V':   return "void";
    case 'Z':   return "boolean";
    default:
                return "UNKNOWN";
    }
}

static char* descriptorToDot(const char* str)
{
    int targetLen = strlen(str);
    int offset = 0;
    int arrayDepth = 0;
    char* newStr;

    /* strip leading [s; will be added to end */
    while (targetLen > 1 && str[offset] == '[') {
        offset++;
        targetLen--;
    }
    arrayDepth = offset;

    if (targetLen == 1) {
        /* primitive type */
        str = primitiveTypeLabel(str[offset]);
        offset = 0;
        targetLen = strlen(str);
    } else {
        /* account for leading 'L' and trailing ';' */
        if (targetLen >= 2 && str[offset] == 'L' &&
            str[offset+targetLen-1] == ';')
        {
            targetLen -= 2;
            offset++;
        }
    }

    newStr = (char*)malloc(targetLen + arrayDepth * 2 +1);

    /* copy class name over */
    int i;
    for (i = 0; i < targetLen; i++) {
        char ch = str[offset + i];
        newStr[i] = (ch == '/' || ch == '$') ? '.' : ch;
    }

    /* add the appropriate number of brackets for arrays */
    while (arrayDepth-- > 0) {
        newStr[i++] = '[';
        newStr[i++] = ']';
    }
    newStr[i] = '\0';
    assert(i == targetLen + arrayDepth * 2);

    return newStr;
}

/* (documented in header file) */
bool dexHasValidMagic(const DexHeader* pHeader) {
	const u1* magic = pHeader->magic;
	const u1* version = &magic[4];

	if (memcmp(magic, DEX_MAGIC, 4) != 0) {
		LOGI("ERROR: unrecognized magic number (%02x %02x %02x %02x)", magic[0],
				magic[1], magic[2], magic[3]);
		return false;
	}

	if ((memcmp(version, DEX_MAGIC_VERS, 4) != 0)
			&& (memcmp(version, DEX_MAGIC_VERS_API_13, 4) != 0)) {
		/*
		 * Magic was correct, but this is an unsupported older or
		 * newer format variant.
		 */
		LOGI("ERROR: unsupported dex version (%02x %02x %02x %02x)", version[0],
				version[1], version[2], version[3]);
		return false;
	}

	return true;
}

static u4 mapTypeToBitMask(int mapType) {
	switch (mapType) {
	case kDexTypeHeaderItem:
		return 1 << 0;
	case kDexTypeStringIdItem:
		return 1 << 1;
	case kDexTypeTypeIdItem:
		return 1 << 2;
	case kDexTypeProtoIdItem:
		return 1 << 3;
	case kDexTypeFieldIdItem:
		return 1 << 4;
	case kDexTypeMethodIdItem:
		return 1 << 5;
	case kDexTypeClassDefItem:
		return 1 << 6;
	case kDexTypeMapList:
		return 1 << 7;
	case kDexTypeTypeList:
		return 1 << 8;
	case kDexTypeAnnotationSetRefList:
		return 1 << 9;
	case kDexTypeAnnotationSetItem:
		return 1 << 10;
	case kDexTypeClassDataItem:
		return 1 << 11;
	case kDexTypeCodeItem:
		return 1 << 12;
	case kDexTypeStringDataItem:
		return 1 << 13;
	case kDexTypeDebugInfoItem:
		return 1 << 14;
	case kDexTypeAnnotationItem:
		return 1 << 15;
	case kDexTypeEncodedArrayItem:
		return 1 << 16;
	case kDexTypeAnnotationsDirectoryItem:
		return 1 << 17;
	default: {
		LOGI("Unknown map item type %04x", mapType);
		return 0;
	}
	}
}

static bool isDataSectionType(int mapType) {
	switch (mapType) {
	case kDexTypeHeaderItem:
	case kDexTypeStringIdItem:
	case kDexTypeTypeIdItem:
	case kDexTypeProtoIdItem:
	case kDexTypeFieldIdItem:
	case kDexTypeMethodIdItem:
	case kDexTypeClassDefItem: {
		return false;
	}
	}

	return true;
}

static bool verifyUlebs(const u1* pData, const u1* pLimit, u4 count) {
	bool okay = true;
	u4 i;

	while (okay && (count-- != 0)) {
		readAndVerifyUnsignedLeb128(&pData, pLimit, &okay);
	}

	return okay;
}

void dexReadClassDataHeader(const u1** pData, DexClassDataHeader *pHeader) {
	pHeader->staticFieldsSize = readUnsignedLeb128(pData);
	pHeader->instanceFieldsSize = readUnsignedLeb128(pData);
	pHeader->directMethodsSize = readUnsignedLeb128(pData);
	pHeader->virtualMethodsSize = readUnsignedLeb128(pData);
}

bool dexReadAndVerifyClassDataHeader(const u1** pData, const u1* pLimit,
		DexClassDataHeader *pHeader) {
	if (!verifyUlebs(*pData, pLimit, 4)) {
		return false;
	}

	dexReadClassDataHeader(pData, pHeader);
	return true;
}

void dexReadClassDataField(const u1** pData, DexField* pField, u4* lastIndex) {
	u4 index = *lastIndex + readUnsignedLeb128(pData);

	pField->accessFlags = readUnsignedLeb128(pData);
	pField->fieldIdx = index;
	*lastIndex = index;
}

bool dexReadAndVerifyClassDataField(const u1** pData, const u1* pLimit,
		DexField* pField, u4* lastIndex) {
	if (!verifyUlebs(*pData, pLimit, 2)) {
		return false;
	}

	dexReadClassDataField(pData, pField, lastIndex);
	return true;
}

void dexReadClassDataMethod(const u1** pData, DexMethod* pMethod,
		u4* lastIndex) {
	u4 index = *lastIndex + readUnsignedLeb128(pData);

	pMethod->accessFlags = readUnsignedLeb128(pData);
	pMethod->codeOff = readUnsignedLeb128(pData);
	pMethod->methodIdx = index;
	*lastIndex = index;
}

bool dexReadAndVerifyClassDataMethod(const u1** pData, const u1* pLimit,
        DexMethod* pMethod, u4* lastIndex) {
    if (! verifyUlebs(*pData, pLimit, 3)) {
        return false;
    }

    dexReadClassDataMethod(pData, pMethod, lastIndex);
    return true;
}

DexClassData* dexReadAndVerifyClassData(const u1** pData, const u1* pLimit) {
	DexClassDataHeader header;
	u4 lastIndex;

	if (*pData == NULL) {
		DexClassData* result = (DexClassData*) malloc(sizeof(DexClassData));
		memset(result, 0, sizeof(*result));
		return result;
	}

	if (!dexReadAndVerifyClassDataHeader(pData, pLimit, &header)) {
		return NULL;
	}

	size_t resultSize = sizeof(DexClassData)
			+ (header.staticFieldsSize * sizeof(DexField))
			+ (header.instanceFieldsSize * sizeof(DexField))
			+ (header.directMethodsSize * sizeof(DexMethod))
			+ (header.virtualMethodsSize * sizeof(DexMethod));

	DexClassData* result = (DexClassData*) malloc(resultSize);
	u1* ptr = ((u1*) result) + sizeof(DexClassData);
	bool okay = true;
	u4 i;

	if (result == NULL) {
		return NULL;
	}

	result->header = header;

	if (header.staticFieldsSize != 0) {
		result->staticFields = (DexField*) ptr;
		ptr += header.staticFieldsSize * sizeof(DexField);
	} else {
		result->staticFields = NULL;
	}

	if (header.instanceFieldsSize != 0) {
		result->instanceFields = (DexField*) ptr;
		ptr += header.instanceFieldsSize * sizeof(DexField);
	} else {
		result->instanceFields = NULL;
	}

	if (header.directMethodsSize != 0) {
		result->directMethods = (DexMethod*) ptr;
		ptr += header.directMethodsSize * sizeof(DexMethod);
	} else {
		result->directMethods = NULL;
	}

	if (header.virtualMethodsSize != 0) {
		result->virtualMethods = (DexMethod*) ptr;
	} else {
		result->virtualMethods = NULL;
	}

	lastIndex = 0;
	for (i = 0; okay && (i < header.staticFieldsSize); i++) {
		okay = dexReadAndVerifyClassDataField(pData, pLimit,
				&result->staticFields[i], &lastIndex);
	}

	lastIndex = 0;
	for (i = 0; okay && (i < header.instanceFieldsSize); i++) {
		okay = dexReadAndVerifyClassDataField(pData, pLimit,
				&result->instanceFields[i], &lastIndex);
	}

	lastIndex = 0;
	for (i = 0; okay && (i < header.directMethodsSize); i++) {
		okay = dexReadAndVerifyClassDataMethod(pData, pLimit,
				&result->directMethods[i], &lastIndex);
	}

	lastIndex = 0;
	for (i = 0; okay && (i < header.virtualMethodsSize); i++) {
		okay = dexReadAndVerifyClassDataMethod(pData, pLimit,
				&result->virtualMethods[i], &lastIndex);
	}

	if (!okay) {
		free(result);
		return NULL;
	}

	return result;
}

const DexTypeId* dexGetTypeId(const DexFile* pDexFile, u4 idx) {
	assert(idx < pDexFile->pHeader->typeIdsSize);
	return &pDexFile->pTypeIds[idx];
}

const DexStringId* dexGetStringId(const DexFile* pDexFile, u4 idx) {
	assert(idx < pDexFile->pHeader->stringIdsSize);
	return &pDexFile->pStringIds[idx];
}

const char* dexGetStringData(const DexFile* pDexFile,
		const DexStringId* pStringId) {
	const u1* ptr = pDexFile->baseAddr + pStringId->stringDataOff;

	// Skip the uleb128 length.
	while (*(ptr++) > 0x7f)
		/* empty */;

	return (const char*) ptr;
}

const char* dexStringById(const DexFile* pDexFile, u4 idx) {
	const DexStringId* pStringId = dexGetStringId(pDexFile, idx);
	return dexGetStringData(pDexFile, pStringId);
}

const char* dexStringByTypeIdx(const DexFile* pDexFile, u4 idx) {
	const DexTypeId* typeId = dexGetTypeId(pDexFile, idx);
	return dexStringById(pDexFile, typeId->descriptorIdx);
}

void dexFileSetupBasicPointers(DexFile* pDexFile, const u1* data) {
	DexHeader *pHeader = (DexHeader*) data;

	pDexFile->baseAddr = data;
	pDexFile->pHeader = pHeader;
	pDexFile->pStringIds = (const DexStringId*) (data + pHeader->stringIdsOff);
	pDexFile->pTypeIds = (const DexTypeId*) (data + pHeader->typeIdsOff);
	pDexFile->pFieldIds = (const DexFieldId*) (data + pHeader->fieldIdsOff);
	pDexFile->pMethodIds = (const DexMethodId*) (data + pHeader->methodIdsOff);
	pDexFile->pProtoIds = (const DexProtoId*) (data + pHeader->protoIdsOff);
	pDexFile->pClassDefs = (const DexClassDef*) (data + pHeader->classDefsOff);
	pDexFile->pLinkData = (const DexLink*) (data + pHeader->linkOff);
}

static bool isValidPointer(const void* ptr, const void* start, const void* end)
{
    return (ptr >= start) && (ptr < end) && (((uintptr_t) ptr & 7) == 0);
}

bool dexParseOptData(const u1* data, size_t length, DexFile* pDexFile)
{
    const void* pOptStart = data + pDexFile->pOptHeader->optOffset;
    const void* pOptEnd = data + length;
    const u4* pOpt = (const u4*) pOptStart;
    u4 optLength = (const u1*) pOptEnd - (const u1*) pOptStart;

    /*
     * Make sure the opt data start is in range and aligned. This may
     * seem like a superfluous check, but (a) if the file got
     * truncated, it might turn out that pOpt >= pOptEnd; and (b)
     * if the opt data header got corrupted, pOpt might not be
     * properly aligned. This test will catch both of these cases.
     */
    if (!isValidPointer(pOpt, pOptStart, pOptEnd)) {
        LOGI("Bogus opt data start pointer");
        return false;
    }

    /* Make sure that the opt data length is a whole number of words. */
    if ((optLength & 3) != 0) {
        LOGI("Unaligned opt data area end");
        return false;
    }

    /*
     * Make sure that the opt data area is large enough to have at least
     * one chunk header.
     */
    if (optLength < 8) {
        LOGI("Undersized opt data area (%u)", optLength);
        return false;
    }

    /* Process chunks until we see the end marker. */
    while (*pOpt != kDexChunkEnd) {
        if (!isValidPointer(pOpt + 2, pOptStart, pOptEnd)) {
            LOGI("Bogus opt data content pointer at offset %u",
                    ((const u1*) pOpt) - data);
            return false;
        }

        u4 size = *(pOpt + 1);
        const u1* pOptData = (const u1*) (pOpt + 2);

        /*
         * The rounded size is 64-bit aligned and includes +8 for the
         * type/size header (which was extracted immediately above).
         */
        u4 roundedSize = (size + 8 + 7) & ~7;
        const u4* pNextOpt = pOpt + (roundedSize / sizeof(u4));

        if (!isValidPointer(pNextOpt, pOptStart, pOptEnd)) {
            LOGI("Opt data area problem for chunk of size %u at offset %u",
                    size, ((const u1*) pOpt) - data);
            return false;
        }

        switch (*pOpt) {
        case kDexChunkClassLookup:
            pDexFile->pClassLookup = (const DexClassLookup*) pOptData;
            break;
        case kDexChunkRegisterMaps:
            LOGI("+++ found register maps, size=%u", size);
            pDexFile->pRegisterMapPool = pOptData;
            break;
        default:
            LOGI("Unknown chunk 0x%08x (%c%c%c%c), size=%d in opt data area",
                *pOpt,
                (char) ((*pOpt) >> 24), (char) ((*pOpt) >> 16),
                (char) ((*pOpt) >> 8),  (char)  (*pOpt),
                size);
            break;
        }

        pOpt = pNextOpt;
    }

    return true;
}

void dexFileFree(DexFile* pDexFile)
{
    if (pDexFile == NULL)
        return;

    free(pDexFile);
}

DexFile* dexFileParse(const u1* data, size_t length, int flags)
{
    DexFile* pDexFile = NULL;
    const DexHeader* pHeader;
    const u1* magic;
    int result = -1;

    if (length < sizeof(DexHeader)) {
        LOGI("too short to be a valid .dex");
        goto file_parse_bail;      /* bad file format */
    }

    pDexFile = (DexFile*) malloc(sizeof(DexFile));
    if (pDexFile == NULL)
        goto file_parse_bail;      /* alloc failure */
    memset(pDexFile, 0, sizeof(DexFile));

    if (memcmp(data, DEX_OPT_MAGIC, 4) == 0) {
        magic = data;
        if (memcmp(magic+4, DEX_OPT_MAGIC_VERS, 4) != 0) {
            LOGI("bad opt version (0x%02x %02x %02x %02x)",
                 magic[4], magic[5], magic[6], magic[7]);
            goto file_parse_bail;
        }

        pDexFile->pOptHeader = (const DexOptHeader*) data;
        LOGI("Good opt header, DEX offset is %d, flags=0x%02x",
            pDexFile->pOptHeader->dexOffset, pDexFile->pOptHeader->flags);

        /* parse the optimized dex file tables */
        if (!dexParseOptData(data, length, pDexFile))
            goto file_parse_bail;

        /* ignore the opt header and appended data from here on out */
        data += pDexFile->pOptHeader->dexOffset;
        length -= pDexFile->pOptHeader->dexOffset;
        if (pDexFile->pOptHeader->dexLength > length) {
            LOGI("File truncated? stored len=%d, rem len=%d",
                pDexFile->pOptHeader->dexLength, (int) length);
            goto file_parse_bail;
        }
        length = pDexFile->pOptHeader->dexLength;
    }

    dexFileSetupBasicPointers(pDexFile, data);
    pHeader = pDexFile->pHeader;

    if (!dexHasValidMagic(pHeader)) {
        goto file_parse_bail;
    }

    if (pHeader->fileSize != length) {
        LOGI("ERROR: stored file size (%d) != expected (%d)",
            (int) pHeader->fileSize, (int) length);
        if (!(flags & kDexParseContinueOnError))
            goto file_parse_bail;
    }

    if (pHeader->classDefsSize == 0) {
        LOGI("ERROR: DEX file has no classes in it, failing");
        goto file_parse_bail;
    }

    /*
     * Success!
     */
    result = 0;

file_parse_bail:
    if (result != 0 && pDexFile != NULL) {
        dexFileFree(pDexFile);
        pDexFile = NULL;
    }
    return pDexFile;
}

const DexClassDef* dexGetClassDef(const DexFile* pDexFile, u4 idx) {
    assert(idx < pDexFile->pHeader->classDefsSize);
    return &pDexFile->pClassDefs[idx];
}

const u1* dexGetClassData(const DexFile* pDexFile,
    const DexClassDef* pClassDef)
{
    if (pClassDef->classDataOff == 0)
        return NULL;
    return (const u1*) (pDexFile->baseAddr + pClassDef->classDataOff);
}

const DexCode* dexGetCode(const DexFile* pDexFile,
    const DexMethod* pDexMethod)
{
    if (pDexMethod->codeOff == 0)
        return NULL;
    return (const DexCode*) (pDexFile->baseAddr + pDexMethod->codeOff);
}

void processDexFile(DexFile* pDexFile, node* classes)
{
    char* package = NULL;
    int i;

    for (i = 0; i < (int) pDexFile->pHeader->classDefsSize; i++) {
		const DexClassDef* pClassDef;
		DexClassData* pClassData = NULL;
		const u1* pEncodedData;
		const char* classDescriptor;
		bool modify = false;

		pClassDef = dexGetClassDef(pDexFile, i);
		pEncodedData = dexGetClassData(pDexFile, pClassDef);
    	pClassData = dexReadAndVerifyClassData(&pEncodedData, NULL);

		classDescriptor = dexStringByTypeIdx(pDexFile, pClassDef->classIdx);

		char* className = NULL;
		className = descriptorToDot(classDescriptor);

		struct node* temp;
		temp = classlist;

		while(temp != NULL) {
			char* cur_name = temp->classname;
			if(strcmp(cur_name, className) != 0)
				temp = temp->nextnode;
			else {
				modify = true;
				break;
			}
		}
		if(modify == true) {
			for (int i = 0; i < (int) pClassData->header.directMethodsSize; i++) {
				const DexCode* pCode = dexGetCode(pDexFile, &pClassData->directMethods[i]);
				const u2* insns;
				int insnIdx;
				insns = pCode->insns;
				insnIdx = 0;
				while (insnIdx < (int) pCode->insnsSize) {
					u2* i = (u2*)insns;
					*i ^= (0xffff);
					insns += 1;
					insnIdx += 1;
				}
			}
			for (int i = 0; i < (int) pClassData->header.virtualMethodsSize; i++) {
				const DexCode* pCode = dexGetCode(pDexFile, &pClassData->virtualMethods[i]);
				const u2* insns;
				int insnIdx;
				insns = pCode->insns;
				insnIdx = 0;
				while (insnIdx < (int) pCode->insnsSize) {
					u2* i = (u2*)insns;
					*i ^= (0xffff);
					insns += 1;
					insnIdx += 1;
				}
			}
			modifyNum++;
			modify = false;
		}

		free(pClassData);
    }
}

JNIEXPORT void JNICALL Java_net_johnpwood_android_standuptimer_jni_DexdumpJNI_DexDump
  (JNIEnv *env, jobject obj)
{
	//const char* dumpdexPath = "/sdcard/dumpdex.txt";	//dexdump info file path
	const char* inputfile = "/sdcard/input.txt";
	char dexfilePath[BUF];		//dex file path

	LOGI("MyJNI is called!");

	int pid = getpid();
	char cmd[BUF];
	sprintf(cmd, "cat /proc/%d/maps", pid);

	FILE* fmap;
	fmap = popen(cmd, "r");

	char start[9];
	char end[9];
	unsigned long addrstart = 0;
	unsigned long addrend = 0;

	if(fmap != NULL){
		char info[BUF];
		char des[BUF];
		char* strBegin = "/data/dalvik-cache";
		char* strEnd = "@classes.dex";
		while(feof(fmap) == 0){
			fgets(info, BUF, fmap);
			if(strlen(info) > 49){
				strcpy(des, info+49);
				if(strstr(des, strBegin)!=NULL && strstr(des, strEnd)!=NULL){
					int len = strlen(des);
					des[len-1] = '\0';
					strcpy(dexfilePath, des);
					strncpy(start, info, 8);
					start[8] = '\0';
					strncpy(end, info+9, 8);
					end[8] = '\0';
					addrstart = strtoul(start, NULL, 16);
					addrend = strtoul(end, NULL, 16);
				}
			}
		}
	}

	fclose(fmap);
	LOGI("find the dex file:%s\n", dexfilePath);

	//FILE* fp = fopen(dumpdexPath, "w+");
	FILE* fp = fopen(inputfile, "r");
	char ch;
	char src[2];
	src[1] = '\0';
	if(fp == NULL) {
		LOGI("the inputfile open failed.\n");
	}
	else {
		LOGI("the inputfile open success.\n");
		node* classnode = new node();

		memset(classnode->classname, '\0', BUF);
		classnode->nextnode = NULL;

		// the classes info are saved in the classlist linked list
		classlist = classnode;
		current = classnode;

		do {
			ch = fgetc(fp);

			if (ch == '\r') {
				ch = fgetc(fp);
			}
			if (ch == '\n') {
				node* anode = new node();
				memset(anode->classname, '\0', BUF);
				anode->nextnode = NULL;

				current->nextnode = anode;
				current = anode;
				while (ch == '\n') {
					ch = fgetc(fp);
				}
			}
			if (!feof(fp)) {
				src[0] = ch;
				strcat(current->classname, src);
			}
 		} while (!feof(fp));

		bool verbose = true;
		bool disassemble = true;
		bool showSectionHeaders = true;

		DexFile* pDexFile = NULL;
		MemMapping map;
		bool mapped = false;

		LOGI("Processing '%s'...\n", dexfilePath);

		map.baseAddr = map.addr = (void *)addrstart;
		map.baseLength = map.length = (size_t)(addrend - addrstart);

		mapped = true;
		int flags = kDexParseVerifyChecksum;

		if(mprotect(map.baseAddr, map.baseLength, PROT_WRITE) < 0){
			LOGI("mprotect(%p, %d, PROT_WRITE) failed", map.baseAddr, map.baseLength);
		}

		pDexFile = dexFileParse((u1*)map.addr, map.length, kDexParseDefault);
		if (pDexFile == NULL) {
			LOGI("Warning: DEX parse failed for '%s'\n", dexfilePath);
			goto bail;
		}

		modifyNum = 0;
		processDexFile(pDexFile, classlist);
		LOGI("the number of modified classes: %d\n", modifyNum);

		if(mprotect(map.baseAddr, map.baseLength, PROT_READ) < 0){
			LOGI("mprotect(%p, %d, PROT_READ) failed", map.baseAddr, map.baseLength);
		}

bail:
		if (mapped){
			map.baseAddr = NULL;
			map.length = 0;
		}

		current = classlist;
		while(current->nextnode != NULL) {
			classlist = current;
			current = current->nextnode;
			delete classlist;
		}
		delete current;
		classlist = NULL;
		current = NULL;

		if (pDexFile != NULL)
			dexFileFree(pDexFile);

		fclose(fp);
		fp = NULL;

		return;
	}
}

